# --coding:utf-8--

# Copyright 2021 xpstem.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


from threading import Event
from lelink.ev3type import *
from lelink.ev3command import *


# 处理回复的等待和同步
class Reply:
    __slots__ = ("counter",
                 "_callback",
                 "_lock_event",
                 "data")

    def __init__(self, counter: int, callback):
        self.counter = counter
        self._callback = callback
        self._lock_event = None
        self.data = None

    def wait_response(self, timeout: int) -> None:
        self._lock_event = Event()
        self._lock_event.clear()
        self._lock_event.wait(timeout)

    def response(self, data: bytearray) -> None:
        self.data = data
        if self._lock_event is not None:
            # 同步响应
            self._lock_event.set()
        if self._callback is not None:
            # 异步响应
            reply_package = EV3ReplyPackage(self.counter, self.data)
            self._callback(reply_package)


class BaseConnection:
    __slots__ = ("_count",
                 "_command_map",
                 "_reply_map")

    def __init__(self):
        self._count = 0
        self._command_map = {}
        self._reply_map = {}
        return

    def connect(self) -> None:
        pass

    def disconnect(self) -> None:
        pass

    def send(self, command: EV3Command, callback=None) -> None:
        self._count = self._count + 1
        self._command_map[self._count] = command

        pkg: EV3CommandPackage = command.message()
        pkg.counter = self._count

        payload: bytearray = pkg.package()
        data_len = len(payload)

        print("{}, request data: {}".format(command.description(), payload.hex('_')))

        ba = bytearray()
        Data16(data_len).write_to(ba)
        ba.extend(payload)

        wait_reply = (callback is None) and (command.command_type == CommandType.SYSTEM_COMMAND_REPLY or
                                             command.command_type == CommandType.DIRECT_COMMAND_REPLY)

        reply = Reply(self._count, callback)
        self._reply_map[self._count] = reply
        try:
            self._do_send(ba)

            if not wait_reply:
                return

            reply.wait_response(15)

            if reply.data is None:
                raise "no reply message."

            self.response(reply)
        finally:
            self._reply_map.pop(self._count)

    def _do_send(self, data: bytearray, wait_reply: bool, reply: Reply) -> None:
        # 由具体子类继承
        pass

    def response_data(self, counter: int, data: bytearray) -> None:
        print("response data: {}".format(data.hex("_")))
        reply = self._reply_map.get(counter)
        if reply is not None:
            reply.response(data)

    def response(self, reply: Reply) -> None:
        if reply.counter in self._command_map:
            cmd = self._command_map.get(reply.counter)
            print("{} response data: {}".format(cmd.description(), reply.data.hex("_")))
            reply_package = EV3ReplyPackage(reply.counter, reply.data)
            cmd.response(reply_package)
        else:
            print("don't found command response data from brick: {}", reply.data.hex("_"))
